import os
import django
import logging
import threading
import time
from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from django.utils import timezone

# 设置Django环境
def setup_django():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cdn_preheat.settings')
    django.setup()

# 导入模型
def import_models():
    from preheat.models import AutoTask, TaskExecutionResult, RequestUrl
    from preheat.views import execute_manual_preheat as execute_preheat
    return AutoTask, TaskExecutionResult, RequestUrl, execute_preheat

# 执行自动任务的函数
def execute_auto_task(task_id):
    logger = logging.getLogger(__name__)
    try:
        setup_django()
        AutoTask, TaskExecutionResult, RequestUrl, execute_preheat = import_models()
        
        task = AutoTask.objects.get(id=task_id)
        logger.info(f"开始执行自动任务: {task.name} (ID: {task_id})")
        
        # 创建执行结果记录
        result = TaskExecutionResult(
            task=task,
            execution_time=timezone.now(),
            success=True
        )
        
        # 模拟手动预热的执行过程
        # 注意：在实际使用中，应该重构预热逻辑，使其可被自动和手动任务共用
        failed_urls = []
        urls_to_preheat = []
        
        # 收集所有需要预热的URL
        for request_url in task.request_urls.all():
            full_url = f"{request_url.cdn_domain.domain}{request_url.url}"
            urls_to_preheat.append(full_url)
        
        total_urls = len(urls_to_preheat) * task.servers.count()  # 每个URL在每个服务器上执行一次
        success_count = 0
        failed_count = 0
        
        # 记录详细的执行情况
        execution_details = []
        
        # 在所有关联的服务器上执行预热
        for server in task.servers.all():
            try:
                import paramiko
                # 使用paramiko进行SSH连接
                client = paramiko.SSHClient()
                client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                logger.info(f"尝试连接到服务器: {server.ip}:{server.ssh_port}")
                client.connect(
                    hostname=server.ip,
                    port=server.ssh_port,
                    username='root',  # 假设使用root用户
                    password=server.ssh_password
                )
                
                logger.info(f"成功连接到服务器: {server.ip}:{server.ssh_port}")
                
                # 在服务器上创建临时目录
                stdin, stdout, stderr = client.exec_command('mkdir -p /tmp/cdn_preheat && cd /tmp/cdn_preheat')
                
                # 下载每个URL
                for url in urls_to_preheat:
                    try:
                        # 先获取HTTP状态码
                        status_code_command = f'curl -o /dev/null -s -w "%{{http_code}}" "{url}"'
                        logger.info(f"检查服务器 {server.ip} 上 {url} 的HTTP状态码")
                        stdin_status, stdout_status, stderr_status = client.exec_command(status_code_command)
                        status_code = stdout_status.read().decode('utf-8').strip()
                        
                        # 检查状态码是否符合要求
                        if status_code in ['200', '206', '304']:
                            # 状态码符合要求，执行下载
                            download_command = f'wget -q "{url}" -O /dev/null'
                            logger.info(f"在服务器 {server.ip} 上下载 {url} (状态码: {status_code})")
                            stdin_download, stdout_download, stderr_download = client.exec_command(download_command)
                            exit_code = stdout_download.channel.recv_exit_status()
                            
                            if exit_code != 0:
                                error_msg = stderr_download.read().decode('utf-8')
                                logger.error(f"在服务器 {server.ip} 上下载失败: {error_msg}")
                                failed_urls.append(f"{server.ip}:{url} (Download Error: {error_msg})")
                                execution_details.append(f"下载失败: {server.ip} - {url} - {error_msg}")
                                failed_count += 1
                            else:
                                logger.info(f"成功在服务器 {server.ip} 上下载 {url}")
                                execution_details.append(f"成功: {server.ip} - {url}")
                                success_count += 1
                        else:
                            # 状态码不符合要求，记录异常
                            logger.warning(f"由于状态码 {status_code}，跳过服务器 {server.ip} 上的 {url}")
                            failed_urls.append(f"{server.ip}:{url} (Abnormal Status Code: {status_code})")
                            execution_details.append(f"异常: {server.ip} - {url} - 状态码: {status_code}")
                            failed_count += 1
                    except Exception as e:
                        logger.exception(f"在服务器 {server.ip} 上下载 {url} 时发生异常")
                        failed_urls.append(f"{server.ip}:{url} (Exception: {str(e)})")
                        execution_details.append(f"异常: {server.ip} - {url} - {str(e)}")
                        failed_count += 1
                
                # 关闭SSH连接
                client.close()
            except Exception as e:
                logger.exception(f"连接到服务器 {server.ip}:{server.ssh_port} 失败")
                for url in urls_to_preheat:
                    failed_urls.append(f"{server.ip}:{url} (SSH连接错误: {str(e)})")
                    execution_details.append(f"连接失败: {server.ip} - {url} - {str(e)}")
                    failed_count += 1
        
        # 更新执行结果
        if failed_urls:
            result.success = False
            result.failed_count = failed_count
            result.success_count = success_count
            result.failed_urls = '\n'.join(failed_urls)
            logger.warning(f"自动任务 {task.name} 执行完成，但有 {failed_count} 个URL执行失败")
            # 打印所有失败的CDN地址
            for failed_url in failed_urls:
                logger.warning(f"  - {failed_url}")
        else:
            result.success = True
            result.failed_count = 0
            result.success_count = success_count
            logger.info(f"自动任务 {task.name} 执行成功，成功预热了 {success_count} 个URL")
        
        # 更新任务的上次执行时间
        task.last_executed_time = timezone.now()
        task.save()
        
        # 打印总体执行统计
        logger.info(f"自动任务执行统计: 总URL数={total_urls}, 成功={success_count}, 失败={failed_count}")
        
        result.save()
        
    except Exception as e:
        logger.exception(f"执行自动任务 {task_id} 时发生严重错误")

# 全局调度器实例
scheduler = None

# 获取调度器实例
def get_scheduler():
    global scheduler
    if scheduler is None or not scheduler.running:
        # 创建新的调度器实例
        scheduler = BackgroundScheduler()
        if not scheduler.running:
            setup_django()
            scheduler.start()
    return scheduler

# 从调度器中删除作业
def remove_job_from_scheduler(task_id):
    logger = logging.getLogger(__name__)
    try:
        global scheduler
        if scheduler and scheduler.running:
            job_id = f"auto_task_{task_id}"
            if scheduler.get_job(job_id):
                scheduler.remove_job(job_id)
                logger.info(f"已从调度器中删除作业: {job_id}")
    except Exception as e:
        logger.exception(f"从调度器中删除作业时发生错误: {str(e)}")

# 将单个任务添加到调度器
def add_task_to_scheduler(task_id):
    logger = logging.getLogger(__name__)
    try:
        # 设置Django环境
        setup_django()
        AutoTask, _, _, _ = import_models()
        
        # 获取任务对象
        task = AutoTask.objects.get(id=task_id)
        
        # 只有激活的任务才添加到调度器
        if not task.is_active:
            logger.info(f"任务 {task.name} (ID: {task_id}) 未激活，不添加到调度器")
            return
        
        # 获取调度器实例
        global scheduler
        scheduler = get_scheduler()
        
        # 根据任务类型设置调度间隔
        if task.task_type == 'minute':
            seconds = task.interval * 60
        elif task.task_type == 'hour':
            seconds = task.interval * 3600
        elif task.task_type == 'day':
            seconds = task.interval * 86400
        else:
            logger.warning(f"任务 {task.name} (ID: {task_id}) 的类型 {task.task_type} 无效，不添加到调度器")
            return
        
        # 创建触发器
        trigger = IntervalTrigger(seconds=seconds)
        
        # 添加任务到调度器
        job_id = f"auto_task_{task.id}"
        scheduler.add_job(
            execute_auto_task,
            trigger,
            args=[task.id],
            id=job_id,
            name=task.name,
            replace_existing=True
        )
        
        logger.info(f"已将任务添加到调度器: {task.name} (ID: {task.id}) - 每 {task.interval} {task.get_task_type_display()}")
        
    except Exception as e:
        logger.exception(f"添加任务 {task_id} 到调度器时发生错误: {str(e)}")

# 初始化和启动调度器
def start_scheduler():
    # 配置日志
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler("cdn_preheat.log"),
            logging.StreamHandler()
        ]
    )
    
    logger = logging.getLogger(__name__)
    logger.info("正在启动自动任务调度器...")
    
    # 设置Django环境
    setup_django()
    
    # 导入模型
    AutoTask, _, _, _ = import_models()
    
    # 获取或创建调度器
    global scheduler
    scheduler = get_scheduler()
    logger.info("调度器已启动")
    
    try:
        # 加载所有激活的自动任务
        tasks = AutoTask.objects.filter(is_active=True)
        logger.info(f"找到 {tasks.count()} 个激活的自动任务")
        
        # 为每个任务添加调度
        for task in tasks:
            # 根据任务类型设置调度间隔
            if task.task_type == 'minute':
                seconds = task.interval * 60
            elif task.task_type == 'hour':
                seconds = task.interval * 3600
            elif task.task_type == 'day':
                seconds = task.interval * 86400
            else:
                logger.warning(f"任务 {task.name} 的类型 {task.task_type} 无效，跳过该任务")
                continue
            
            # 创建触发器
            trigger = IntervalTrigger(seconds=seconds)
            
            # 添加任务到调度器
            job_id = f"auto_task_{task.id}"
            scheduler.add_job(
                execute_auto_task,
                trigger,
                args=[task.id],
                id=job_id,
                name=task.name,
                replace_existing=True
            )
            
            logger.info(f"已添加任务到调度器: {task.name} (ID: {task.id}) - 每 {task.interval} {task.get_task_type_display()}")
        
        # 保持调度器运行
        logger.info("自动任务调度器已完成初始化，将持续运行...")
        while True:
            time.sleep(1)
    except (KeyboardInterrupt, SystemExit):
        # 优雅关闭调度器
        scheduler.shutdown()
        logger.info("调度器已关闭")
    except Exception as e:
        logger.exception("调度器运行过程中发生异常")
        scheduler.shutdown()

# 重启调度器函数
def restart_scheduler():
    logger = logging.getLogger(__name__)
    logger.info("开始重启调度器...")
    
    try:
        # 停止当前运行的调度器
        global scheduler
        if scheduler and scheduler.running:
            logger.info("正在停止当前调度器...")
            scheduler.shutdown()
            logger.info("当前调度器已停止")
        
        # 重新创建调度器实例
        scheduler = None
        setup_django()
        scheduler = get_scheduler()
        
        # 导入模型
        AutoTask, _, _, _ = import_models()
        
        # 清空所有现有作业
        if scheduler and scheduler.running:
            jobs = scheduler.get_jobs()
            for job in jobs:
                scheduler.remove_job(job.id)
            logger.info(f"已清空 {len(jobs)} 个现有作业")
        
        # 重新加载所有激活的自动任务
        tasks = AutoTask.objects.filter(is_active=True)
        logger.info(f"重新加载 {tasks.count()} 个激活的自动任务")
        
        # 为每个任务重新添加调度
        for task in tasks:
            # 根据任务类型设置调度间隔
            if task.task_type == 'minute':
                seconds = task.interval * 60
            elif task.task_type == 'hour':
                seconds = task.interval * 3600
            elif task.task_type == 'day':
                seconds = task.interval * 86400
            else:
                logger.warning(f"任务 {task.name} 的类型 {task.task_type} 无效，跳过该任务")
                continue
            
            # 创建触发器
            trigger = IntervalTrigger(seconds=seconds)
            
            # 添加任务到调度器
            job_id = f"auto_task_{task.id}"
            scheduler.add_job(
                execute_auto_task,
                trigger,
                args=[task.id],
                id=job_id,
                name=task.name,
                replace_existing=True
            )
            
            logger.info(f"已重新添加任务到调度器: {task.name} (ID: {task.id}) - 每 {task.interval} {task.get_task_type_display()}")
        
        logger.info("调度器重启成功！")
        return True, "调度器重启成功！"
    except Exception as e:
        logger.exception("调度器重启过程中发生异常")
        return False, f"调度器重启失败: {str(e)}"

if __name__ == "__main__":
    start_scheduler()